using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;

namespace LFromeWork
{
    public class AssetBundleLoader : IDisposable
    {
        private int loadCount = 10;

        LinkedList<AssetBundleRefEntity> exitABs = null;
        LinkedList<AssetBundleRefEntity> waitABs = null;
        LinkedList<AssetBundleRefEntity> loadingABs = null;

        public AssetBundleLoader()
        {
            exitABs = new LinkedList<AssetBundleRefEntity>();
            waitABs = new LinkedList<AssetBundleRefEntity>();
            loadingABs = new LinkedList<AssetBundleRefEntity>();
        }

        /// <summary>
        /// 异步加载AB
        /// </summary>
        /// <param name="assetBundlePath"></param>
        /// <returns></returns>
        public Task<AssetBundle> LoadAssetBundleAsync(string assetBundlePath)
        {
            //添加TaskCompletionSource ，在外部调用次方时，形成顺序调用，不用回调
            var task = new TaskCompletionSource<AssetBundle>();
            LoadAssetBundleAsync(assetBundlePath, (AssetBundle bundle) => { task.TrySetResult(bundle); });
            return task.Task;
        }

        /// <summary>
        /// 异步加载AB
        /// </summary>
        /// <param name="abPath"></param>
        /// <param name="onLoadAssetBundleComplete"></param>
        private void LoadAssetBundleAsync(string abPath, Action<AssetBundle> onLoadAssetBundleComplete)
        {
            //0.先判断是否已经 存在bundle
            AssetBundleRefEntity entity = GetAssetBundleEntity(abPath, exitABs);
            if (entity != null)
            {
                entity.ReferenceAdd();
                onLoadAssetBundleComplete(entity.Target);
                return;
            }

            //1.先检查是否已经在等待中
            entity = GetAssetBundleEntity(abPath, waitABs);

            //2.如果不再等待中 检查是否已经在加载中
            if (entity == null)
                entity = GetAssetBundleEntity(abPath, loadingABs);

            //3.如果在任意一个队列中，添加引用计数
            if (entity != null)
            {
                entity.OnLoadAssetBundleComplete += onLoadAssetBundleComplete;
                entity.ReferenceAdd();
                return;
            }

            //4.不存在任何队列中的话创建
            entity = AssetBundleRefEntity.Create(abPath);
            entity.OnLoadAssetBundleComplete += onLoadAssetBundleComplete;
            entity.ReferenceAdd();
            waitABs.AddLast(entity);

            //真正调用加载的地方在 CheckWaitAssetBundleStatus
            //因为需要 添加 最大加载 数量
        }

        /// <summary>
        /// 释放AB
        /// </summary>
        /// <param name="abPath"></param>
        public void ReleaseAssetBundle(string abPath)
        {
            //0.先判断是否已经 存在bundle
            AssetBundleRefEntity entity = GetAssetBundleEntity(abPath, exitABs);

            //1.如果不再已加载完成 检查是否已经在等待中
            if (entity == null)
                entity = GetAssetBundleEntity(abPath, waitABs);

            //2.如果不再等待中 检查是否已经在加载中
            if (entity == null)
                entity = GetAssetBundleEntity(abPath, loadingABs);

            //3.引用计数减减
            if (entity != null)
            {
                entity.ReferenceRemove();
            }
        }

        /// <summary>
        /// 更具 abPath 获取 AssetBundleRefEntity
        /// </summary>
        /// <param name="abPath"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        private AssetBundleRefEntity GetAssetBundleEntity(string abPath, LinkedList<AssetBundleRefEntity> list)
        {
            for (LinkedListNode<AssetBundleRefEntity> curr = list.First; curr != null; curr = curr.Next)
            {
                if (curr.Value.AssetBundlePath == abPath)
                {
                    return curr.Value;
                }
            }

            return null;
        }

        /// <summary>
        /// 检查 ab异步加载状态
        /// </summary>
        public void OnUpdate()
        {
            //异步加载是否完成
            CheckLoadingAssetBundleStatus();

            //等待中的是否可以进行异步加载
            CheckWaitAssetBundleStatus();

            //当前是否有可释放的
            CheckExitAssetBundleStatus();
        }

        /// <summary>
        /// 检查加载中ab 的状态
        /// </summary>
        private void CheckLoadingAssetBundleStatus()
        {
            LinkedListNode<AssetBundleRefEntity> currLoad = loadingABs.First;
            LinkedListNode<AssetBundleRefEntity> next = null;
            while (currLoad != null)
            {
                if (currLoad.Value.CurrAssetBundleCreateRequest !=null || currLoad.Value.CurrAssetBundleCreateRequest.isDone)
                {
                    next = currLoad.Next;
                    loadingABs.Remove(currLoad.Value);
                    exitABs.AddLast(currLoad.Value);
                    currLoad.Value.UpdateAssetBundleCreateRequest();
                    currLoad = next;
                }
                else
                    currLoad = currLoad.Next;
            }
        }

        /// <summary>
        /// 检查 等待中的ab状态
        /// </summary>
        private void CheckWaitAssetBundleStatus()
        {
            //添加 等待加载的ab
            int canLoadCount = loadCount - loadingABs.Count;
            if (canLoadCount > 0)
            {
                for (int i = 0; i < canLoadCount; i++)
                {
                    if (waitABs.First != null)
                    {
                        LinkedListNode<AssetBundleRefEntity> waitEntity = waitABs.First;
                        waitABs.RemoveFirst();

                        waitEntity.Value.LoadAssetBundleAsync();
                        loadingABs.AddLast(waitEntity.Value);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// 检查已经加载好的ab状态
        /// </summary>
        private void CheckExitAssetBundleStatus()
        {
            LinkedListNode<AssetBundleRefEntity> currExit = exitABs.First;
            LinkedListNode<AssetBundleRefEntity> next = null;

            while (currExit != null)
            {
                if (currExit.Value.GetCanRelease())
                {
                    currExit.Value.Release();
                    next = currExit.Next;
                    exitABs.Remove(currExit.Value);
                    currExit = next;
                }
                else
                    currExit = currExit.Next;
            }
        }
        
        public void Dispose()
        {
            exitABs.Clear();
            exitABs = null;
            waitABs.Clear();
            waitABs = null;
            loadingABs.Clear();
            loadingABs = null;
        }
    }
}
